style: refine agent generation workspace dark ui
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-18 02:25:36 +08:00
parent d6411076fa
commit 55774ef43e
7 changed files with 183 additions and 86 deletions

View File

@ -168,6 +168,33 @@
}
@layer utilities {
.agent-dark-scrollbar {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.18) rgba(255, 255, 255, 0.04);
}
.agent-dark-scrollbar::-webkit-scrollbar {
width: 10px;
height: 10px;
}
.agent-dark-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.04);
border-radius: 999px;
}
.agent-dark-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.18);
border: 2px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
border-radius: 999px;
}
.agent-dark-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.26);
background-clip: padding-box;
}
.step {
counter-increment: step;
}

View File

@ -550,29 +550,18 @@ export function AgentGenerationWorkspaceDarkV2({
{/* Main Content */}
<main className="flex-1 overflow-y-auto overscroll-contain pt-16 lg:overflow-hidden">
<div className="mx-auto flex min-h-full max-w-[1480px] flex-col px-6 py-6 lg:h-full lg:px-8 xl:px-10">
{/* 步骤指示器 */}
<motion.div
className="mb-6 flex items-center justify-center gap-2 text-sm"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
>
<span className="text-white/40">第一步:风格选择</span>
<span className="text-white/20">→</span>
<span className="text-emerald-400 font-medium">第二步:生图模式</span>
</motion.div>
<div className="grid min-h-0 flex-1 grid-cols-1 gap-6 lg:grid-cols-[400px_minmax(0,1fr)]">
{/* Left Panel - Controls */}
<div className="min-h-0 space-y-4 lg:h-full lg:overflow-y-auto lg:pr-2">
<div className="agent-dark-scrollbar min-h-0 space-y-4 lg:h-full lg:overflow-y-auto lg:pr-2">
{/* 嵌入的智能体选择面板 */}
{!selectedAgentPreset && presetPanel && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="rounded-2xl border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
className="rounded-[28px] border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
>
<h3 className="mb-3 text-sm font-semibold text-white">选择生图模式</h3>
<div className="max-h-[480px] overflow-y-auto pr-1">
<div className="agent-dark-scrollbar max-h-[480px] overflow-y-auto pr-1">
{presetPanel}
</div>
</motion.div>
@ -581,7 +570,7 @@ export function AgentGenerationWorkspaceDarkV2({
{/* 已选智能体信息 */}
{selectedAgentPreset && (
<motion.div
className="rounded-2xl border border-white/12 bg-white/[0.05] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
className="rounded-[28px] border border-white/12 bg-white/[0.05] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
>
@ -591,10 +580,10 @@ export function AgentGenerationWorkspaceDarkV2({
<img
src={selectedAgentPreset.avatarUrl}
alt={selectedAgentPreset.name}
className="h-10 w-10 rounded-lg object-cover"
className="h-10 w-10 rounded-xl object-cover"
/>
) : (
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-emerald-500/18">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-500/18">
<Wand2 className="h-5 w-5 text-emerald-300" />
</div>
)}
@ -636,7 +625,7 @@ export function AgentGenerationWorkspaceDarkV2({
{/* Image Slots */}
<motion.section
className="rounded-2xl border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
className="rounded-[28px] border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
@ -655,7 +644,7 @@ export function AgentGenerationWorkspaceDarkV2({
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
className={cn(
"group relative aspect-square overflow-hidden rounded-xl transition-all",
"group relative aspect-square overflow-hidden rounded-[24px] transition-all",
attachments[index]
? "ring-2 ring-emerald-400/45"
: "border-2 border-dashed border-white/15 bg-black/25 hover:border-emerald-400/35"
@ -707,7 +696,7 @@ export function AgentGenerationWorkspaceDarkV2({
))}
</div>
) : (
<div className="flex min-h-[132px] flex-col items-center justify-center rounded-2xl border border-dashed border-white/12 bg-black/20 px-4 text-center text-sm text-white/45">
<div className="flex min-h-[132px] flex-col items-center justify-center rounded-[24px] border border-dashed border-white/12 bg-black/20 px-4 text-center text-sm text-white/45">
<ImagePlus className="mb-2 h-5 w-5 opacity-60" />
先选择一个生图模式,系统会自动展示产品图1、产品图2和模特图槽位。
</div>
@ -716,7 +705,7 @@ export function AgentGenerationWorkspaceDarkV2({
{/* Prompt Input */}
<motion.section
className="rounded-2xl border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
className="rounded-[28px] border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
@ -729,13 +718,13 @@ export function AgentGenerationWorkspaceDarkV2({
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="描述你想要的风格、氛围、构图..."
className="min-h-[100px] resize-none rounded-lg border-white/12 bg-black text-sm text-white placeholder:text-white/20 focus:border-emerald-400/30"
className="min-h-[100px] resize-none rounded-[24px] border-white/12 bg-black text-sm text-white placeholder:text-white/20 focus:border-emerald-400/30"
/>
</motion.section>
{/* Settings */}
<motion.section
className="rounded-2xl border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
className="rounded-[28px] border border-white/12 bg-white/[0.04] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
@ -746,7 +735,7 @@ export function AgentGenerationWorkspaceDarkV2({
</h3>
<div className="space-y-3">
<Select value={selectedModelId} onValueChange={setSelectedModelId}>
<SelectTrigger className="h-10 rounded-lg border-white/12 bg-black text-white">
<SelectTrigger className="h-10 rounded-[24px] border-white/12 bg-black text-white">
<SelectValue placeholder="选择模型" />
</SelectTrigger>
<SelectContent className="border-white/10 bg-zinc-900">
@ -763,6 +752,8 @@ export function AgentGenerationWorkspaceDarkV2({
initialParams={imageParams}
onParamsChange={setImageParams}
inline
inlineTheme="dark"
showInlineDivider={false}
providerType={currentModel?.providerType}
imageGenConfig={currentModel?.imageGenConfig || undefined}
/>
@ -771,7 +762,7 @@ export function AgentGenerationWorkspaceDarkV2({
onClick={handleSend}
disabled={isGenerating || !selectedAgentPresetId}
className={cn(
"h-11 w-full gap-2 rounded-lg",
"h-11 w-full gap-2 rounded-[24px]",
"bg-gradient-to-r from-emerald-500 to-teal-500",
"text-white font-medium",
"hover:from-emerald-400 hover:to-teal-400",
@ -810,11 +801,18 @@ export function AgentGenerationWorkspaceDarkV2({
生成完成后会同步回到画布,并在这里保留预览与切换。
</div>
</div>
{allGeneratedImages.length > 0 ? (
<div className="rounded-full bg-white/8 px-3 py-1 text-xs text-white/60">
共 {allGeneratedImages.length} 张
<div className="flex items-center gap-3">
<div className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-1 text-xs text-white/58">
第一步:风格选择
<span className="mx-1.5 text-white/22">→</span>
<span className="font-medium text-emerald-300">第二步:生图模式</span>
</div>
) : null}
{allGeneratedImages.length > 0 ? (
<div className="rounded-full bg-white/8 px-3 py-1 text-xs text-white/60">
共 {allGeneratedImages.length} 张
</div>
) : null}
</div>
</div>
<AnimatePresence mode="wait">
@ -845,7 +843,7 @@ export function AgentGenerationWorkspaceDarkV2({
<img
src={buildPreviewImageUrl(displayedResultUrl, 1400)}
alt="生成结果"
className="max-h-full max-w-full cursor-zoom-in rounded-lg object-contain"
className="max-h-full max-w-full cursor-zoom-in rounded-[24px] object-contain"
onClick={() => setFullscreenPreview(displayedResultUrl)}
/>
<button
@ -891,9 +889,9 @@ export function AgentGenerationWorkspaceDarkV2({
{allGeneratedImages.map((url) => (
<button
key={url}
onClick={() => setSelectedResultUrl(url)}
className={cn(
"relative flex-shrink-0 overflow-hidden rounded-xl transition-all",
onClick={() => setSelectedResultUrl(url)}
className={cn(
"relative flex-shrink-0 overflow-hidden rounded-[24px] transition-all",
displayedResultUrl === url
? "ring-2 ring-emerald-400"
: "ring-1 ring-white/12 hover:ring-white/30"

View File

@ -47,7 +47,7 @@ export function AgentWorkflowBrandModelPicker({
return (
<section
className={cn(
"space-y-3 rounded-2xl border p-4",
"space-y-3 rounded-[28px] border p-4",
isDark ? "border-white/12 bg-white/[0.04]" : "border-[#dbe3ff] bg-[#f8faff]"
)}
>
@ -74,7 +74,7 @@ export function AgentWorkflowBrandModelPicker({
{!normalizedBrand ? (
<div
className={cn(
"flex min-h-[124px] flex-col items-center justify-center rounded-2xl border border-dashed px-4 text-center text-sm",
"flex min-h-[124px] flex-col items-center justify-center rounded-[24px] border border-dashed px-4 text-center text-sm",
isDark
? "border-white/12 bg-black/20 text-white/45"
: "bg-background/60 text-muted-foreground"
@ -146,7 +146,7 @@ export function AgentWorkflowBrandModelPicker({
)
}
className={cn(
"group overflow-hidden rounded-2xl border bg-background text-left transition-all",
"group overflow-hidden rounded-[24px] border bg-background text-left transition-all",
selected
? isDark
? "border-emerald-400/70 bg-white/[0.03] shadow-[0_0_0_1px_rgba(52,211,153,0.2)]"
@ -206,7 +206,7 @@ export function AgentWorkflowBrandModelPicker({
) : (
<div
className={cn(
"flex min-h-[124px] flex-col items-center justify-center rounded-2xl border border-dashed px-4 text-center text-sm",
"flex min-h-[124px] flex-col items-center justify-center rounded-[24px] border border-dashed px-4 text-center text-sm",
isDark
? "border-white/12 bg-black/20 text-white/45"
: "bg-background/60 text-muted-foreground"

View File

@ -70,7 +70,7 @@ export function AgentWorkflowMaterialPicker({
return (
<section
className={cn(
"space-y-3 rounded-2xl border p-4",
"space-y-3 rounded-[24px] border p-4",
isDark ? "border-white/12 bg-black/20" : "bg-muted/20"
)}
>
@ -122,7 +122,7 @@ export function AgentWorkflowMaterialPicker({
{disabled && disabledHint ? (
<div
className={cn(
"rounded-xl border px-3 py-2 text-xs",
"rounded-[20px] border px-3 py-2 text-xs",
isDark
? "border-amber-500/20 bg-amber-500/10 text-amber-100"
: "border-amber-200 bg-amber-50 text-amber-800"
@ -133,7 +133,7 @@ export function AgentWorkflowMaterialPicker({
) : null}
{!disabled && errorText ? (
<div className="rounded-xl border border-destructive/20 bg-destructive/5 px-3 py-2 text-xs text-destructive">
<div className="rounded-[20px] border border-destructive/20 bg-destructive/5 px-3 py-2 text-xs text-destructive">
{errorText}
</div>
) : null}
@ -208,7 +208,7 @@ export function AgentWorkflowMaterialPicker({
type="button"
onClick={() => handleToggleImage(image.id)}
className={cn(
"group overflow-hidden rounded-2xl border bg-background text-left transition-all",
"group overflow-hidden rounded-[24px] border bg-background text-left transition-all",
selected
? isDark
? "border-emerald-400/70 bg-white/[0.03] shadow-[0_0_0_1px_rgba(52,211,153,0.2)]"
@ -271,7 +271,7 @@ export function AgentWorkflowMaterialPicker({
) : (
<div
className={cn(
"flex min-h-[124px] flex-col items-center justify-center rounded-2xl border border-dashed px-4 text-center text-sm",
"flex min-h-[124px] flex-col items-center justify-center rounded-[24px] border border-dashed px-4 text-center text-sm",
isDark
? "border-white/12 bg-white/[0.03] text-white/42"
: "bg-background/60 text-muted-foreground"
@ -299,7 +299,7 @@ export function AgentWorkflowMaterialPicker({
{result?.images?.length ? (
<div
className={cn(
"flex items-start gap-2 rounded-xl px-3 py-2 text-xs",
"flex items-start gap-2 rounded-[20px] px-3 py-2 text-xs",
isDark ? "bg-white/[0.04] text-white/45" : "bg-background text-muted-foreground"
)}
>

View File

@ -116,7 +116,7 @@ export function AgentWorkflowMaterialSelectionPanel({
return (
<div
className={cn(
"space-y-3 rounded-2xl border p-4",
"space-y-3 rounded-[28px] border p-4",
isDark ? "border-white/12 bg-white/[0.04]" : "border-[#dbe3ff] bg-[#f8faff]"
)}
>

View File

@ -31,7 +31,7 @@ export function AgentWorkflowShowcaseBanner({
const resolvedActionLabel = actionLabel || resolved.actionText;
return (
<div className={cn("space-y-8", className)}>
<div className={cn("space-y-12 pt-6 md:space-y-14 md:pt-8", className)}>
<motion.div
className="text-center"
initial={{ opacity: 0, y: 20 }}
@ -39,7 +39,7 @@ export function AgentWorkflowShowcaseBanner({
transition={{ duration: 0.6 }}
>
<h1 className="mb-3 text-3xl font-bold text-white md:text-4xl">{resolved.title}</h1>
<p className="mb-6 text-base text-white/50">{resolved.subtitle}</p>
<p className="mb-8 text-base text-white/50 md:mb-9">{resolved.subtitle}</p>
{resolvedActionLabel || secondaryActionLabel ? (
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
{resolvedActionLabel ? (
@ -64,7 +64,7 @@ export function AgentWorkflowShowcaseBanner({
type="button"
variant="outline"
onClick={onSecondaryActionClick}
className="h-12 rounded-2xl border-white/20 bg-transparent px-12 text-base font-medium text-white hover:border-emerald-400/50 hover:bg-white/[0.04]"
className="h-12 rounded-full border-white/20 bg-transparent px-12 text-base font-medium text-white hover:border-emerald-400/50 hover:bg-white/[0.04]"
>
{secondaryActionLabel}
</Button>

View File

@ -33,6 +33,8 @@ interface ImageParamsBarProps {
providerType?: string; // 模型类型:gemini, volcano-vision, alibaba-image, ruxa-image 等
hideImageCount?: boolean; // 隐藏图片数量按钮(仅个别页面需要)
hideAdvancedOptions?: boolean; // 隐藏高级设置按钮
inlineTheme?: "light" | "dark";
showInlineDivider?: boolean;
// 🆕 图片生成模型特性配置(从模型配置中读取)
imageGenConfig?: {
maxImages?: number;
@ -83,6 +85,8 @@ export function ImageParamsBar({
providerType = "gemini", // 默认 gemini
hideImageCount = false,
hideAdvancedOptions = false,
inlineTheme = "light",
showInlineDivider = true,
imageGenConfig,
}: ImageParamsBarProps) {
const getImageCountOptions = () => {
@ -255,24 +259,41 @@ export function ImageParamsBar({
// 内联模式:只返回参数按钮,嵌入工具栏
if (inline) {
const isDarkInline = inlineTheme === "dark";
const ratioLabel = params.aspectRatio === "auto" ? "智能比例" : params.aspectRatio;
const sizeLabel = params.imageSize;
const outputSize = getComputedOutputSize(params.aspectRatio, params.imageSize);
return (
<div className="ml-1 flex items-center gap-1 border-l border-border/50 pl-1">
<div
className={cn(
"ml-1 flex items-center gap-1 pl-1",
showInlineDivider &&
(isDarkInline ? "border-l border-white/10" : "border-l border-border/50")
)}
>
<Popover>
<PopoverTrigger asChild>
<Button
variant="ghost"
className="h-8 max-w-[168px] shrink rounded-[12px] border border-[#d6dbe2] bg-[#f3f4f6]/90 px-2 text-[11px] font-semibold text-[#2f3b48] hover:bg-[#edeff2]/90"
className={cn(
"h-8 max-w-[168px] shrink rounded-[12px] px-2 text-[11px] font-semibold",
isDarkInline
? "border border-white/12 bg-white/[0.06] text-white hover:bg-white/[0.1]"
: "border border-[#d6dbe2] bg-[#f3f4f6]/90 text-[#2f3b48] hover:bg-[#edeff2]/90"
)}
title={`比例:${params.aspectRatio},分辨率:${params.imageSize}`}
>
<span className="mr-1 flex h-3.5 w-3.5 items-center justify-center text-[#20262f]">
<span
className={cn(
"mr-1 flex h-3.5 w-3.5 items-center justify-center",
isDarkInline ? "text-white/80" : "text-[#20262f]"
)}
>
{renderRatioGlyph(params.aspectRatio)}
</span>
<span className="truncate">{ratioLabel}</span>
<span className="mx-1.5 h-3 w-px bg-[#cfd5de]" />
<span className={cn("mx-1.5 h-3 w-px", isDarkInline ? "bg-white/12" : "bg-[#cfd5de]")} />
<span className="inline-flex min-w-0 items-center gap-0.5 truncate">
{sizeLabel}
</span>
@ -282,21 +303,35 @@ export function ImageParamsBar({
align="center"
side="top"
sideOffset={10}
className="w-[312px] max-w-[calc(100vw-24px)] rounded-[14px] border border-[#d8dde4] bg-[#f3f4f6]/90 p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]"
className={cn(
"w-[312px] max-w-[calc(100vw-24px)] rounded-[14px] p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]",
isDarkInline
? "border border-white/12 bg-[#0d1013]/98 text-white backdrop-blur-xl"
: "border border-[#d8dde4] bg-[#f3f4f6]/90"
)}
>
<div className="space-y-2.5">
<div>
<p className="text-[11px] font-medium text-[#8a94a3]">选择比例</p>
<div className="mt-1.5 grid grid-cols-4 gap-1.5 rounded-[12px] bg-[#eceff2]/90 p-1.5 md:grid-cols-5">
<p className={cn("text-[11px] font-medium", isDarkInline ? "text-white/45" : "text-[#8a94a3]")}>选择比例</p>
<div
className={cn(
"mt-1.5 grid grid-cols-4 gap-1.5 rounded-[12px] p-1.5 md:grid-cols-5",
isDarkInline ? "bg-white/[0.04]" : "bg-[#eceff2]/90"
)}
>
{ASPECT_RATIO_OPTIONS.map((option) => (
<button
key={option.value}
type="button"
className={cn(
"flex h-[46px] flex-col items-center justify-center rounded-[10px] border text-[#5f6b7a] transition",
"flex h-[46px] flex-col items-center justify-center rounded-[10px] border transition",
params.aspectRatio === option.value
? "border-white bg-white text-[#1f2937] shadow-[0_1px_5px_rgba(15,23,42,0.08)]"
: "border-transparent hover:bg-white/70"
? isDarkInline
? "border-emerald-400/50 bg-emerald-500/12 text-emerald-200 shadow-[0_1px_5px_rgba(15,23,42,0.18)]"
: "border-white bg-white text-[#1f2937] shadow-[0_1px_5px_rgba(15,23,42,0.08)]"
: isDarkInline
? "border-transparent text-white/62 hover:bg-white/[0.06]"
: "border-transparent text-[#5f6b7a] hover:bg-white/70"
)}
onClick={() => updateParam("aspectRatio", option.value)}
>
@ -312,8 +347,13 @@ export function ImageParamsBar({
</div>
<div>
<p className="text-[11px] font-medium text-[#8a94a3]">选择分辨率</p>
<div className="mt-1.5 grid grid-cols-2 gap-1.5 rounded-[12px] bg-[#eceff2]/90 p-1">
<p className={cn("text-[11px] font-medium", isDarkInline ? "text-white/45" : "text-[#8a94a3]")}>选择分辨率</p>
<div
className={cn(
"mt-1.5 grid grid-cols-2 gap-1.5 rounded-[12px] p-1",
isDarkInline ? "bg-white/[0.04]" : "bg-[#eceff2]/90"
)}
>
{availableSizeOptions.map((option) => (
<button
key={option.value}
@ -321,8 +361,12 @@ export function ImageParamsBar({
className={cn(
"h-8 rounded-[9px] text-[12px] font-semibold transition",
params.imageSize === option.value
? "bg-white text-[#111827] shadow-[0_1px_5px_rgba(15,23,42,0.08)]"
: "bg-transparent text-[#5f6b7a] hover:bg-white/70"
? isDarkInline
? "bg-emerald-500/12 text-emerald-200 shadow-[0_1px_5px_rgba(15,23,42,0.18)]"
: "bg-white text-[#111827] shadow-[0_1px_5px_rgba(15,23,42,0.08)]"
: isDarkInline
? "bg-transparent text-white/62 hover:bg-white/[0.06]"
: "bg-transparent text-[#5f6b7a] hover:bg-white/70"
)}
onClick={() => updateParam("imageSize", option.value)}
>
@ -335,22 +379,22 @@ export function ImageParamsBar({
</div>
<div>
<p className="text-[11px] font-medium text-[#8a94a3]">尺寸</p>
<p className={cn("text-[11px] font-medium", isDarkInline ? "text-white/45" : "text-[#8a94a3]")}>尺寸</p>
<div className="mt-1.5 grid grid-cols-[1fr_auto_1fr_auto] items-center gap-1.5">
<div className="h-7 rounded-[9px] border border-[#dce1e8] bg-[#eef1f4]/90 px-2 text-[#b0b8c4]">
<div className={cn("h-7 rounded-[9px] px-2", isDarkInline ? "border border-white/10 bg-white/[0.04] text-white/68" : "border border-[#dce1e8] bg-[#eef1f4]/90 text-[#b0b8c4]")}>
<div className="flex h-full items-center justify-between gap-1.5">
<span className="text-[10px] font-semibold">W</span>
<span className="text-[12px] font-semibold">{outputSize.width}</span>
</div>
</div>
<Link2 className="h-3 w-3 text-[#adb6c3]" />
<div className="h-7 rounded-[9px] border border-[#dce1e8] bg-[#eef1f4]/90 px-2 text-[#b0b8c4]">
<Link2 className={cn("h-3 w-3", isDarkInline ? "text-white/35" : "text-[#adb6c3]")} />
<div className={cn("h-7 rounded-[9px] px-2", isDarkInline ? "border border-white/10 bg-white/[0.04] text-white/68" : "border border-[#dce1e8] bg-[#eef1f4]/90 text-[#b0b8c4]")}>
<div className="flex h-full items-center justify-between gap-1.5">
<span className="text-[10px] font-semibold">H</span>
<span className="text-[12px] font-semibold">{outputSize.height}</span>
</div>
</div>
<span className="text-[11px] font-semibold text-[#8a94a3]">PX</span>
<span className={cn("text-[11px] font-semibold", isDarkInline ? "text-white/45" : "text-[#8a94a3]")}>PX</span>
</div>
</div>
</div>
@ -362,23 +406,34 @@ export function ImageParamsBar({
<PopoverTrigger asChild>
<Button
variant="ghost"
className="h-8 min-w-[56px] rounded-[12px] border border-[#d6dbe2] bg-[#f3f4f6]/90 px-2.5 text-[12px] font-semibold text-[#2f3b48] hover:bg-[#edeff2]/90 focus-visible:ring-0 focus-visible:ring-offset-0"
className={cn(
"h-8 min-w-[56px] rounded-[12px] px-2.5 text-[12px] font-semibold focus-visible:ring-0 focus-visible:ring-offset-0",
isDarkInline
? "border border-white/12 bg-white/[0.06] text-white hover:bg-white/[0.1]"
: "border border-[#d6dbe2] bg-[#f3f4f6]/90 text-[#2f3b48] hover:bg-[#edeff2]/90"
)}
title={`数量:${params.imageCount || 1}张`}
>
<Sparkles className="mr-1 h-3.5 w-3.5 text-[#6b7280]" />
<Sparkles className={cn("mr-1 h-3.5 w-3.5", isDarkInline ? "text-emerald-300" : "text-[#6b7280]")} />
{params.imageCount || 1}张
</Button>
</PopoverTrigger>
<PopoverContent
align="center"
side="top"
sideOffset={10}
className="w-auto min-w-[120px] rounded-[14px] border border-[#d8dde4] bg-[#f3f4f6]/95 p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]"
>
side="top"
sideOffset={10}
className={cn(
"w-auto min-w-[120px] rounded-[14px] p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]",
isDarkInline
? "border border-white/12 bg-[#0d1013]/98 text-white backdrop-blur-xl"
: "border border-[#d8dde4] bg-[#f3f4f6]/95"
)}
>
<div>
<p className="text-[11px] font-medium text-[#8a94a3]">生成数量</p>
<p className={cn("text-[11px] font-medium", isDarkInline ? "text-white/45" : "text-[#8a94a3]")}>生成数量</p>
<div className={cn(
"mt-1.5 grid gap-1.5 rounded-[12px] bg-[#eceff2]/90 p-1.5",
"mt-1.5 grid gap-1.5 rounded-[12px] p-1.5",
isDarkInline ? "bg-white/[0.04]" : "bg-[#eceff2]/90",
imageCountOptions.length <= 3 ? "grid-cols-3" : "grid-cols-3"
)}>
{imageCountOptions.map((count) => (
@ -388,8 +443,12 @@ export function ImageParamsBar({
className={cn(
"flex h-8 min-w-[36px] items-center justify-center rounded-[9px] border text-[12px] font-semibold transition",
(params.imageCount || 1) === count
? "border-white bg-white text-[#1f2937] shadow-[0_1px_5px_rgba(15,23,42,0.08)]"
: "border-transparent text-[#5f6b7a] hover:bg-white/70"
? isDarkInline
? "border-emerald-400/50 bg-emerald-500/12 text-emerald-200 shadow-[0_1px_5px_rgba(15,23,42,0.18)]"
: "border-white bg-white text-[#1f2937] shadow-[0_1px_5px_rgba(15,23,42,0.08)]"
: isDarkInline
? "border-transparent text-white/62 hover:bg-white/[0.06]"
: "border-transparent text-[#5f6b7a] hover:bg-white/70"
)}
onClick={() => updateParam("imageCount", count)}
>
@ -409,8 +468,11 @@ export function ImageParamsBar({
<Button
variant="ghost"
className={cn(
"h-8 w-8 shrink-0 rounded-[12px] border border-[#d6dbe2] bg-[#f3f4f6]/90 p-0 text-[#5f6b7a] hover:bg-[#edeff2]/90 focus-visible:ring-0 focus-visible:ring-offset-0",
(params.promptExtend || params.negativePrompt) && "text-primary"
"h-8 w-8 shrink-0 rounded-[12px] p-0 focus-visible:ring-0 focus-visible:ring-offset-0",
isDarkInline
? "border border-white/12 bg-white/[0.06] text-white/70 hover:bg-white/[0.1]"
: "border border-[#d6dbe2] bg-[#f3f4f6]/90 text-[#5f6b7a] hover:bg-[#edeff2]/90",
(params.promptExtend || params.negativePrompt) && (isDarkInline ? "text-emerald-300" : "text-primary")
)}
title="高级设置"
>
@ -420,17 +482,22 @@ export function ImageParamsBar({
</PopoverTrigger>
<PopoverContent
align="end"
side="top"
sideOffset={10}
className="w-[280px] max-w-[calc(100vw-24px)] rounded-[14px] border border-[#d8dde4] bg-[#f3f4f6]/95 p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]"
>
side="top"
sideOffset={10}
className={cn(
"w-[280px] max-w-[calc(100vw-24px)] rounded-[14px] p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]",
isDarkInline
? "border border-white/12 bg-[#0d1013]/98 text-white backdrop-blur-xl"
: "border border-[#d8dde4] bg-[#f3f4f6]/95"
)}
>
<div className="space-y-2.5">
{/* 提示词智能优化 */}
{showPromptExtend && (
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<span className="text-[12px] font-semibold text-[#2f3b48]">提示词优化</span>
<p className="text-[11px] text-[#8a94a3]">AI 自动优化您的描述</p>
<span className={cn("text-[12px] font-semibold", isDarkInline ? "text-white/88" : "text-[#2f3b48]")}>提示词优化</span>
<p className={cn("text-[11px]", isDarkInline ? "text-white/45" : "text-[#8a94a3]")}>AI 自动优化您的描述</p>
</div>
<Switch
checked={params.promptExtend ?? true}
@ -442,12 +509,17 @@ export function ImageParamsBar({
{/* 反向提示词 */}
{showNegativePrompt && (
<div className="space-y-1">
<span className="text-[12px] font-semibold text-[#2f3b48]">反向提示词</span>
<span className={cn("text-[12px] font-semibold", isDarkInline ? "text-white/88" : "text-[#2f3b48]")}>反向提示词</span>
<Input
placeholder="不希望出现的内容,如:低质量、模糊..."
value={params.negativePrompt || ""}
onChange={(e) => updateParam("negativePrompt", e.target.value)}
className="h-7 rounded-[8px] border-[#dce1e8] bg-[#eef1f4]/90 text-[12px] text-[#2f3b48] placeholder:text-[#b0b8c4]"
className={cn(
"h-7 rounded-[8px] text-[12px]",
isDarkInline
? "border-white/12 bg-white/[0.04] text-white placeholder:text-white/28"
: "border-[#dce1e8] bg-[#eef1f4]/90 text-[#2f3b48] placeholder:text-[#b0b8c4]"
)}
/>
</div>
)}