refactor: redesign realtime enhance workspace

This commit is contained in:
Your Name 2026-04-06 23:21:46 +08:00
parent a17a035689
commit cd54fcf792
2 changed files with 218 additions and 135 deletions

View File

@ -5,7 +5,10 @@ import {
ChevronDown,
ImageIcon,
Loader2,
ScanFace,
Send,
Settings2,
SlidersHorizontal,
Sparkles,
} from "lucide-react";
import {
@ -101,34 +104,38 @@ const FALLBACK_CONFIG: EnhanceProClientConfig = {
};
const PANEL_SHELL_CLASS =
"pointer-events-auto rounded-[24px] border border-[#e0e2e8] bg-white shadow-[0_0_0_1px_rgb(224,226,232),0_16px_36px_rgba(28,28,30,0.04)]";
"pointer-events-auto rounded-[30px] border border-[#eadfcb] bg-[linear-gradient(180deg,#fffdf9_0%,#fff7ed_100%)] shadow-[0_0_0_1px_rgba(234,223,203,0.88),0_24px_48px_rgba(15,23,42,0.06)]";
const SOLID_PANEL_FIELD_CLASS =
"border-[#e9eaef] bg-white text-[#1c1c1e] shadow-[0_1px_2px_rgba(28,28,30,0.04)]";
"border-[#eadfcb] bg-[#fffdfa] text-[#1c1c1e] shadow-[inset_0_1px_0_rgba(255,255,255,0.95),0_1px_2px_rgba(28,28,30,0.04)]";
const SOLID_PANEL_CARD_CLASS =
"rounded-[20px] border border-[#e0e2e8] bg-white shadow-[0_0_0_1px_rgb(224,226,232),0_8px_18px_rgba(28,28,30,0.03)]";
"rounded-[22px] border border-white/90 bg-white/78 shadow-[inset_0_1px_0_rgba(255,255,255,0.95),0_10px_22px_rgba(15,23,42,0.05)]";
const PANEL_SECTION_TITLE_CLASS =
"font-semibold text-[12px] tracking-[-0.02em] text-[#1c1c1e]";
"font-semibold text-[15px] tracking-[-0.04em] text-[#1c1c1e]";
const PANEL_SECTION_NOTE_CLASS =
"text-[11px] leading-4 text-[#555a6a]";
"text-[12px] leading-5 text-[#555a6a]";
const SOLID_PANEL_SELECT_CONTENT_CLASS =
"border-[#e0e2e8] bg-white shadow-[0_0_0_1px_rgb(224,226,232),0_16px_34px_rgba(28,28,30,0.08)]";
"border-[#eadfcb] bg-[#fffdfa] shadow-[0_0_0_1px_rgba(234,223,203,0.88),0_18px_38px_rgba(15,23,42,0.08)]";
const PRIMARY_ACTION_BUTTON_CLASS =
"border border-[#5b76fe] bg-[#5b76fe] text-white shadow-[0_14px_28px_rgba(91,118,254,0.24)] transition hover:border-[#2a41b6] hover:bg-[#2a41b6] disabled:border-[#e0e2e8] disabled:bg-[#eef1f6] disabled:text-[#a5a8b5] disabled:shadow-none";
"border border-[#5b76fe] bg-[linear-gradient(180deg,#6d87ff_0%,#516bf5_100%)] text-white shadow-[0_16px_30px_rgba(91,118,254,0.26)] transition hover:border-[#2a41b6] hover:bg-[linear-gradient(180deg,#5d78ff_0%,#2a41b6_100%)] disabled:border-[#e0e2e8] disabled:bg-[#eef1f6] disabled:text-[#a5a8b5] disabled:shadow-none";
const SECONDARY_ACTION_BUTTON_CLASS =
"border border-[#c7cad5] bg-white text-[#1c1c1e] shadow-[0_0_0_1px_rgb(224,226,232),0_8px_18px_rgba(28,28,30,0.03)] transition hover:border-[#5b76fe] hover:bg-[#f6f8ff] hover:text-[#2a41b6] disabled:border-[#e0e2e8] disabled:bg-[#f7f8fb] disabled:text-[#a5a8b5] disabled:shadow-none";
"border border-[#d9e2fb] bg-[#f7f9ff] text-[#3150d3] shadow-[0_0_0_1px_rgba(217,226,251,0.75),0_10px_22px_rgba(91,118,254,0.08)] transition hover:border-[#5b76fe] hover:bg-[#edf2ff] hover:text-[#2a41b6] disabled:border-[#e0e2e8] disabled:bg-[#f7f8fb] disabled:text-[#a5a8b5] disabled:shadow-none";
const COMPACT_FIELD_CLASS = `${SOLID_PANEL_FIELD_CLASS} h-11 rounded-[16px] text-sm`;
const COMPACT_LABEL_CLASS =
"font-medium text-[#555a6a] text-[11px] leading-4";
"font-medium text-[#555a6a] text-[11px] leading-4 tracking-[0.01em]";
const COMPACT_TOGGLE_CARD_CLASS =
"flex items-center justify-between gap-2.5 px-3 py-2.5";
const SLIDER_TRACK_CLASS = "h-2 bg-[#edf0f5] shadow-inner";
"flex items-center justify-between gap-2.5 px-3.5 py-3";
const SLIDER_TRACK_CLASS = "h-2.5 bg-[#e9edf5] shadow-inner";
const SLIDER_RANGE_CLASS = "bg-[#5b76fe]";
const SLIDER_THUMB_CLASS =
"h-4 w-4 border border-[#c7cad5] bg-white shadow-[0_0_0_4px_rgba(91,118,254,0.16)]";
"h-4 w-4 border border-[#d6d1c4] bg-white shadow-[0_0_0_4px_rgba(91,118,254,0.16)]";
const SIDEBAR_SCROLL_CLASS =
"pr-1.5 [scrollbar-width:thin] [scrollbar-color:#d7cfbf_transparent] [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-[#d7cfbf] hover:[&::-webkit-scrollbar-thumb]:bg-[#c8bda9]";
const SIDEBAR_SECTION_CARD_CLASS =
"rounded-[22px] border border-[#ece3d4] bg-[#fffdf8] p-3.5 shadow-[0_10px_24px_rgba(15,23,42,0.05)]";
"rounded-[30px] border p-3.5 shadow-[0_18px_36px_rgba(15,23,42,0.06)]";
const SECTION_EYEBROW_CLASS =
"inline-flex items-center gap-1.5 rounded-full border border-white/90 bg-white/72 px-2.5 py-1 font-semibold text-[10px] tracking-[0.14em] text-[#6b7280] uppercase";
const INNER_PANEL_CLASS =
"rounded-[22px] border border-white/90 bg-white/76 p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.92)]";
function clampNumber(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
@ -277,10 +284,17 @@ function ParameterSliderRow({
const displayValue = formatValue ? formatValue(value) : String(value);
return (
<div className={cn("space-y-2", disabled && "opacity-50")}>
<div
className={cn(
"space-y-3 rounded-[20px] border border-white/80 bg-white/74 px-3.5 py-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.94)]",
disabled && "opacity-50"
)}
>
<div className="flex items-center justify-between gap-3">
<div className="font-medium text-[12px] text-slate-700">{label}</div>
<div className="inline-flex min-w-[50px] justify-center rounded-full border border-[#e7e1d4] bg-[#fffdfa] px-2.5 py-0.5 font-medium text-[11px] text-slate-600 shadow-[0_4px_12px_rgba(15,23,42,0.04)]">
<div className="font-medium text-[12px] text-slate-700 tracking-[-0.01em]">
{label}
</div>
<div className="inline-flex min-w-[54px] justify-center rounded-full border border-[#e7e1d4] bg-[#fffdfa] px-2.5 py-1 font-medium text-[11px] text-slate-600 shadow-[0_4px_12px_rgba(15,23,42,0.04)]">
{displayValue}
</div>
</div>
@ -643,8 +657,10 @@ export function EnhanceProWorkspace({
};
return (
<div className="relative flex h-full w-full bg-white text-[#1c1c1e]">
<div className="relative flex min-w-0 flex-1 items-center justify-center overflow-hidden bg-[radial-gradient(circle_at_16%_16%,rgba(91,118,254,0.09),transparent_26%),radial-gradient(circle_at_84%_20%,rgba(255,230,205,0.65),transparent_18%),radial-gradient(circle_at_72%_86%,rgba(195,250,245,0.55),transparent_20%),#ffffff] pb-24">
<div className="relative flex h-full w-full bg-[linear-gradient(180deg,#fdfbf6_0%,#f7f4ec_100%)] text-[#1c1c1e]">
<div className="relative flex min-w-0 flex-1 items-center justify-center overflow-hidden px-6 pb-28 pt-24">
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_18%_14%,rgba(255,240,190,0.68),transparent_24%),radial-gradient(circle_at_86%_18%,rgba(195,250,245,0.72),transparent_18%),radial-gradient(circle_at_70%_84%,rgba(238,242,255,0.95),transparent_25%),linear-gradient(180deg,#fffdf8_0%,#faf6ef_100%)]" />
<div className="pattern-grid-lg pointer-events-none absolute inset-0 opacity-[0.035] [mask-image:radial-gradient(circle_at_center,black_38%,transparent_82%)]" />
{activePreviewUrl ? (
<div
className={cn(
@ -657,30 +673,72 @@ export function EnhanceProWorkspace({
onPointerMove={handlePreviewPointerMove}
onPointerUp={finishPreviewDrag}
>
<img
alt={activePreviewLabel}
className="pointer-events-none max-h-full max-w-full object-contain transition-transform duration-75"
src={getDisplayImageUrl(activePreviewUrl, 1800)}
style={{
transform: `translate(${previewOffset.x}px, ${previewOffset.y}px)`,
}}
/>
<div className="pointer-events-none absolute top-6 left-6 flex items-center gap-2 rounded-full border border-[#e0e2e8] bg-white/92 px-3 py-1.5 text-[#555a6a] text-xs shadow-[0_0_0_1px_rgb(224,226,232),0_10px_24px_rgba(28,28,30,0.04)] backdrop-blur-sm">
<div className="pointer-events-none absolute top-[12%] left-[10%] h-40 w-40 rounded-full bg-[#fff0be] blur-3xl" />
<div className="pointer-events-none absolute right-[10%] bottom-[12%] h-44 w-44 rounded-full bg-[#c3faf5] blur-3xl" />
<div className="relative flex max-h-full max-w-full items-center justify-center rounded-[38px] border border-[#eadfcb] bg-[linear-gradient(180deg,rgba(255,255,255,0.95),rgba(255,248,238,0.95))] p-4 shadow-[0_0_0_1px_rgba(234,223,203,0.72),0_28px_56px_rgba(15,23,42,0.10)] backdrop-blur-xl">
<div className="overflow-hidden rounded-[30px] border border-white/90 bg-white shadow-[0_20px_36px_rgba(15,23,42,0.10)]">
<img
alt={activePreviewLabel}
className="pointer-events-none max-h-[calc(100vh-17rem)] max-w-[min(1000px,calc(100vw-35rem))] object-contain transition-transform duration-75"
src={getDisplayImageUrl(activePreviewUrl, 1800)}
style={{
transform: `translate(${previewOffset.x}px, ${previewOffset.y}px)`,
}}
/>
</div>
</div>
<div className="pointer-events-none absolute top-6 left-6 flex items-center gap-2 rounded-full border border-[#eadfcb] bg-white/94 px-3.5 py-1.5 text-[#555a6a] text-xs shadow-[0_0_0_1px_rgba(234,223,203,0.75),0_10px_24px_rgba(28,28,30,0.04)] backdrop-blur-sm">
<ImageIcon className="h-3.5 w-3.5" />
<span>{activePreviewLabel}</span>
</div>
<div className="pointer-events-none absolute top-6 right-8 hidden items-center gap-2 rounded-full border border-[#eadfcb] bg-white/90 px-3 py-1.5 text-[#555a6a] text-xs shadow-[0_0_0_1px_rgba(234,223,203,0.72),0_10px_24px_rgba(28,28,30,0.04)] lg:inline-flex">
双击回正
</div>
</div>
) : (
<div className="flex max-w-[28rem] flex-col items-center gap-4 rounded-[28px] border border-[#e0e2e8] bg-white/92 px-8 py-9 text-center shadow-[0_0_0_1px_rgb(224,226,232),0_24px_48px_rgba(28,28,30,0.05)] backdrop-blur-sm">
<div className="flex h-16 w-16 items-center justify-center rounded-[22px] bg-[#eef2ff] text-[#5b76fe] shadow-[0_8px_18px_rgba(91,118,254,0.16)]">
<Sparkles className="h-8 w-8" />
</div>
<div className="font-semibold text-[30px] tracking-[-0.03em] text-[#1c1c1e] leading-[1.08]">
还没有可增强的图片
</div>
<div className="max-w-[24rem] text-[#555a6a] text-[15px] leading-7">
请先在主画布中选中一张图片,再从底部工具栏进入“图像增强 Pro”,
或者先在实时画布模式里生成一张结果后再切回来。
<div className="relative w-full max-w-[42rem] px-6">
<div className="pointer-events-none absolute -top-6 left-8 h-24 w-24 rounded-[32px] bg-[#fff0be] blur-3xl" />
<div className="pointer-events-none absolute right-12 -bottom-4 h-28 w-28 rounded-[32px] bg-[#c3faf5] blur-3xl" />
<div className="relative rounded-[38px] border border-[#eadfcb] bg-[linear-gradient(180deg,rgba(255,255,255,0.97),rgba(255,247,236,0.97))] px-8 py-8 shadow-[0_0_0_1px_rgba(234,223,203,0.8),0_30px_60px_rgba(15,23,42,0.08)] backdrop-blur-sm">
<div className="flex items-start gap-4">
<div className="flex h-16 w-16 shrink-0 items-center justify-center rounded-[24px] border border-white/90 bg-[#fff0be] text-[#7a5b00] shadow-[0_14px_28px_rgba(255,221,117,0.25)]">
<Sparkles className="h-8 w-8" />
</div>
<div className="min-w-0 text-left">
<div className={SECTION_EYEBROW_CLASS}>Enhance Pro Studio</div>
<div className="mt-3 font-semibold text-[34px] tracking-[-0.05em] text-[#1c1c1e] leading-[1.02]">
还没有可增强的图片
</div>
<div className="mt-3 max-w-[30rem] text-[#555a6a] text-[15px] leading-7">
先在主画布里选中一张图片,或者先在实时画布模式里生成结果,
再切回这里进行精修、增强和细节重建。
</div>
</div>
</div>
<div className="mt-7 grid gap-3 md:grid-cols-2">
<div className="rounded-[24px] border border-white/90 bg-white/78 p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.92)]">
<div className="text-[11px] font-semibold tracking-[0.16em] text-[#8c6c1f] uppercase">
Step 01
</div>
<div className="mt-2 font-semibold text-[18px] tracking-[-0.03em] text-[#1c1c1e]">
选中一张图
</div>
<div className="mt-1 text-[13px] leading-6 text-[#555a6a]">
从主画布中选择要增强的图片,系统会自动把它带入当前工作台。
</div>
</div>
<div className="rounded-[24px] border border-white/90 bg-white/78 p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.92)]">
<div className="text-[11px] font-semibold tracking-[0.16em] text-[#0c7270] uppercase">
Step 02
</div>
<div className="mt-2 font-semibold text-[18px] tracking-[-0.03em] text-[#1c1c1e]">
调参后开始增强
</div>
<div className="mt-1 text-[13px] leading-6 text-[#555a6a]">
使用右侧工具卡片快速选择模式、强度和增强范围,生成后可直接发回画布。
</div>
</div>
</div>
</div>
</div>
)}
@ -719,8 +777,8 @@ export function EnhanceProWorkspace({
</div>
) : null}
<div className="pointer-events-none absolute bottom-5 left-1/2 z-30 flex w-fit max-w-[calc(100%-2rem)] -translate-x-1/2 flex-col items-center gap-1.5">
<div className="pointer-events-auto rounded-full border border-[#e0e2e8] bg-white/94 px-3.5 py-1 text-[#555a6a] text-[11px] shadow-[0_0_0_1px_rgb(224,226,232),0_10px_22px_rgba(28,28,30,0.04)] backdrop-blur-xl">
<div className="pointer-events-none absolute bottom-6 left-1/2 z-30 flex w-fit max-w-[calc(100%-2rem)] -translate-x-1/2 flex-col items-center gap-2">
<div className="pointer-events-auto rounded-full border border-[#eadfcb] bg-white/92 px-4 py-1.5 text-[#555a6a] text-[11px] shadow-[0_0_0_1px_rgba(234,223,203,0.75),0_12px_24px_rgba(28,28,30,0.04)] backdrop-blur-xl">
<span className="inline-flex items-center gap-1.5">
{selectedGeneratedImage ? (
<CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />
@ -728,10 +786,10 @@ export function EnhanceProWorkspace({
{actionHint}
</span>
</div>
<div className="pointer-events-auto inline-flex items-center justify-center gap-2 rounded-full border border-[#e0e2e8] bg-white/94 p-1.5 shadow-[0_0_0_1px_rgb(224,226,232),0_16px_32px_rgba(28,28,30,0.06)] backdrop-blur-xl">
<div className="pointer-events-auto inline-flex items-center justify-center gap-2 rounded-[32px] border border-[#eadfcb] bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(255,247,236,0.96))] p-2 shadow-[0_0_0_1px_rgba(234,223,203,0.78),0_18px_38px_rgba(28,28,30,0.08)] backdrop-blur-xl">
<Button
className={cn(
"h-10 min-w-[208px] rounded-full px-5 text-sm",
"h-11 min-w-[214px] rounded-[24px] px-5 text-sm font-medium tracking-[-0.01em]",
PRIMARY_ACTION_BUTTON_CLASS
)}
disabled={isSubmitting || !sourceImageUrl}
@ -747,7 +805,7 @@ export function EnhanceProWorkspace({
</Button>
<Button
className={cn(
"h-10 min-w-[208px] rounded-full px-5 text-sm",
"h-11 min-w-[214px] rounded-[24px] px-5 text-sm font-medium tracking-[-0.01em]",
SECONDARY_ACTION_BUTTON_CLASS
)}
disabled={!selectedGeneratedImage}
@ -762,13 +820,13 @@ export function EnhanceProWorkspace({
</div>
</div>
<div className="relative z-20 h-full w-[408px] max-w-[40vw] shrink-0 py-5 pr-5">
<div className="h-full space-y-3 overflow-y-auto pr-2 pb-3 [scrollbar-color:#c9d2ea_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-[#c9d2ea] [&::-webkit-scrollbar-thumb]:border-[2px] [&::-webkit-scrollbar-thumb]:border-solid [&::-webkit-scrollbar-thumb]:border-transparent">
<div className="relative z-20 h-full w-[420px] max-w-[42vw] shrink-0 py-6 pr-6">
<div className={cn("h-full space-y-3 overflow-y-auto pb-3", SIDEBAR_SCROLL_CLASS)}>
{isLoading ? (
<div
className={cn(
PANEL_SHELL_CLASS,
"flex items-center gap-2 px-3.5 py-2.5 text-muted-foreground text-sm"
"flex items-center gap-2 px-4 py-3 text-muted-foreground text-sm"
)}
>
<Loader2 className="h-4 w-4 animate-spin" />
@ -777,44 +835,56 @@ export function EnhanceProWorkspace({
) : null}
{!config.enabled && !isLoading ? (
<div className="rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-amber-800 text-sm leading-6">
<div className="rounded-[24px] border border-amber-200 bg-[#fff7df] px-4 py-3 text-amber-800 text-sm leading-6 shadow-[0_10px_24px_rgba(255,209,102,0.12)]">
当前未启用图像增强 Pro。请先在系统设置的“本地AI设置”里完成 Krea
配置。
</div>
) : null}
{lastErrorMessage ? (
<div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-rose-700 text-sm leading-6">
<div className="rounded-[24px] border border-rose-200 bg-[#fff3f1] px-4 py-3 text-rose-700 text-sm leading-6 shadow-[0_10px_24px_rgba(244,114,182,0.08)]">
{lastErrorMessage}
</div>
) : null}
<Collapsible
className={cn(PANEL_SHELL_CLASS, "px-3.5 py-3.5")}
className={cn(
PANEL_SHELL_CLASS,
SIDEBAR_SECTION_CARD_CLASS,
"border-[#eadfcb] bg-[linear-gradient(180deg,#fffdf7_0%,#fff6ea_100%)] px-3.5 py-3.5"
)}
onOpenChange={setSettingsOpen}
open={settingsOpen}
>
<CollapsibleTrigger asChild>
<button
className="flex w-full items-center justify-between gap-3 rounded-[18px] bg-[#f7f8fc] px-3 py-2.5 text-left transition hover:bg-[#f1f4fb]"
className="flex w-full items-center justify-between gap-3 rounded-[24px] border border-white/90 bg-white/70 px-3.5 py-3 text-left transition hover:bg-white/82"
type="button"
>
<div className="space-y-0.5">
<div className="text-[10px] font-semibold tracking-[0.14em] text-[#555a6a] uppercase">
Studio
<div className="flex min-w-0 items-center gap-3">
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-[18px] border border-white/90 bg-[#fff0be] text-[#7c5a00] shadow-[0_10px_22px_rgba(255,221,117,0.20)]">
<Settings2 className="h-5 w-5" />
</div>
<div className="min-w-0 space-y-1">
<div className={SECTION_EYEBROW_CLASS}>Studio Setup</div>
<div className={PANEL_SECTION_TITLE_CLASS}>设置</div>
</div>
<div className={PANEL_SECTION_TITLE_CLASS}>设置</div>
</div>
<ChevronDown
className={cn(
"h-4 w-4 text-slate-500 transition-transform",
settingsOpen && "rotate-180"
)}
/>
<div className="flex items-center gap-2">
<div className="hidden rounded-full border border-white/90 bg-white/80 px-2.5 py-1 text-[11px] text-[#555a6a] sm:inline-flex">
{formState.model}
</div>
<ChevronDown
className={cn(
"h-4 w-4 text-slate-500 transition-transform",
settingsOpen && "rotate-180"
)}
/>
</div>
</button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-3 space-y-3">
<div className="space-y-2">
<div className={cn(INNER_PANEL_CLASS, "space-y-2")}>
<Label className={PANEL_SECTION_TITLE_CLASS}>增强预设</Label>
<Select onValueChange={handleSelectPreset} value={selectedPresetId}>
<SelectTrigger className={COMPACT_FIELD_CLASS}>
@ -829,7 +899,7 @@ export function EnhanceProWorkspace({
</SelectContent>
</Select>
{selectedPreset ? (
<div className="rounded-[18px] border border-[#e7ebf4] bg-[#f7f8fc] px-3 py-2.5">
<div className="rounded-[18px] border border-white/90 bg-[#fffdfa] px-3 py-2.5">
<div className={cn(PANEL_SECTION_NOTE_CLASS, "line-clamp-2")}>
{selectedPreset.description ||
`${selectedPreset.model} · ${getSubjectDetectionLabel(selectedPreset.subjectDetection)}`}
@ -838,7 +908,7 @@ export function EnhanceProWorkspace({
) : null}
</div>
<div className="space-y-2">
<div className={cn(INNER_PANEL_CLASS, "space-y-2")}>
<Label
className={PANEL_SECTION_TITLE_CLASS}
htmlFor="enhance-workspace-prompt"
@ -846,7 +916,7 @@ export function EnhanceProWorkspace({
增强提示词
</Label>
<Textarea
className={`${SOLID_PANEL_FIELD_CLASS} min-h-[76px] resize-none rounded-[18px] bg-[#fbfcff] text-sm placeholder:text-[#a5a8b5]`}
className={`${SOLID_PANEL_FIELD_CLASS} min-h-[84px] resize-none rounded-[20px] bg-[#fffdfa] text-sm placeholder:text-[#a5a8b5]`}
id="enhance-workspace-prompt"
onChange={(event) =>
setFormState((prev) => ({
@ -860,7 +930,7 @@ export function EnhanceProWorkspace({
/>
</div>
<div className="grid grid-cols-4 gap-2.5">
<div className={cn(INNER_PANEL_CLASS, "grid grid-cols-4 gap-2.5")}>
<div className="space-y-1.5">
<Label
className={COMPACT_LABEL_CLASS}
@ -1064,7 +1134,7 @@ export function EnhanceProWorkspace({
className={cn(
SOLID_PANEL_CARD_CLASS,
COMPACT_TOGGLE_CARD_CLASS,
"bg-[#fbfcff]"
"bg-white/78"
)}
>
<span className="text-foreground text-xs">启用放大</span>
@ -1082,7 +1152,7 @@ export function EnhanceProWorkspace({
className={cn(
SOLID_PANEL_CARD_CLASS,
COMPACT_TOGGLE_CARD_CLASS,
"bg-[#fbfcff]"
"bg-white/78"
)}
>
<span className="text-foreground text-xs">裁剪铺满</span>
@ -1102,20 +1172,27 @@ export function EnhanceProWorkspace({
{config.enableAdvancedControls ? (
<Collapsible
className={cn(PANEL_SHELL_CLASS, "px-3.5 py-3.5")}
className={cn(
PANEL_SHELL_CLASS,
SIDEBAR_SECTION_CARD_CLASS,
"border-[#dfe6f5] bg-[linear-gradient(180deg,#fbfdff_0%,#f3f7ff_100%)] px-3.5 py-3.5"
)}
onOpenChange={setAdjustmentsOpen}
open={adjustmentsOpen}
>
<CollapsibleTrigger asChild>
<button
className="flex w-full items-center justify-between gap-3 rounded-[18px] bg-[#f7f8fc] px-3 py-2.5 text-left transition hover:bg-[#f1f4fb]"
className="flex w-full items-center justify-between gap-3 rounded-[24px] border border-white/90 bg-white/72 px-3.5 py-3 text-left transition hover:bg-white/84"
type="button"
>
<div className="space-y-0.5">
<div className="text-[10px] font-semibold tracking-[0.14em] text-[#555a6a] uppercase">
Adjust
<div className="flex min-w-0 items-center gap-3">
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-[18px] border border-white/90 bg-[#eef2ff] text-[#5b76fe] shadow-[0_10px_22px_rgba(91,118,254,0.16)]">
<SlidersHorizontal className="h-5 w-5" />
</div>
<div className="min-w-0 space-y-1">
<div className={SECTION_EYEBROW_CLASS}>Fine Tune</div>
<div className={PANEL_SECTION_TITLE_CLASS}>调节</div>
</div>
<div className={PANEL_SECTION_TITLE_CLASS}>调节</div>
</div>
<ChevronDown
className={cn(
@ -1192,21 +1269,28 @@ export function EnhanceProWorkspace({
) : null}
<Collapsible
className={cn(PANEL_SHELL_CLASS, "px-3.5 py-3.5")}
className={cn(
PANEL_SHELL_CLASS,
SIDEBAR_SECTION_CARD_CLASS,
"border-[#f1ddd7] bg-[linear-gradient(180deg,#fffaf7_0%,#fff1eb_100%)] px-3.5 py-3.5"
)}
onOpenChange={setFaceOpen}
open={faceOpen}
>
<div className="flex items-center justify-between gap-3">
<CollapsibleTrigger asChild>
<button
className="flex min-w-0 flex-1 items-center justify-between gap-3 rounded-[18px] bg-[#f7f8fc] px-3 py-2.5 text-left transition hover:bg-[#f1f4fb]"
className="flex min-w-0 flex-1 items-center justify-between gap-3 rounded-[24px] border border-white/90 bg-white/72 px-3.5 py-3 text-left transition hover:bg-white/84"
type="button"
>
<div className="space-y-0.5">
<div className="text-[10px] font-semibold tracking-[0.14em] text-[#555a6a] uppercase">
Face
<div className="flex min-w-0 items-center gap-3">
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-[18px] border border-white/90 bg-[#ffe8df] text-[#b95a36] shadow-[0_10px_22px_rgba(240,150,120,0.18)]">
<ScanFace className="h-5 w-5" />
</div>
<div className="min-w-0 space-y-1">
<div className={SECTION_EYEBROW_CLASS}>Face Boost</div>
<div className={PANEL_SECTION_TITLE_CLASS}>面部增强</div>
</div>
<div className={PANEL_SECTION_TITLE_CLASS}>面部增强</div>
</div>
<ChevronDown
className={cn(

View File

@ -2918,17 +2918,17 @@ export function RealtimeCanvasDialog({
}, [clearPreviewCanvas, closeBrushSettingPopovers, resetCanvas]);
const floatingSurfaceClass =
"border border-[#e0e2e8] bg-white/92 shadow-[0_0_0_1px_rgb(224,226,232),0_16px_36px_rgba(28,28,30,0.05)] backdrop-blur-xl";
"border border-[#eadfcb] bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(255,247,236,0.94))] shadow-[0_0_0_1px_rgba(234,223,203,0.82),0_20px_42px_rgba(15,23,42,0.08)] backdrop-blur-xl";
const controlSurfaceClass =
"border border-[#e9eaef] bg-white text-[#1c1c1e] shadow-[0_1px_2px_rgba(28,28,30,0.04)]";
"border border-[#eadfcb] bg-[#fffdfa] text-[#1c1c1e] shadow-[inset_0_1px_0_rgba(255,255,255,0.95),0_1px_2px_rgba(28,28,30,0.04)]";
const softActionButtonClass =
"border border-[#dbe2ff] bg-[#eef2ff] text-[#5b76fe] shadow-[0_10px_20px_rgba(91,118,254,0.10)] transition hover:bg-[#e7ecff] hover:text-[#2a41b6]";
"border border-[#dbe2ff] bg-[linear-gradient(180deg,#f4f7ff_0%,#e9efff_100%)] text-[#5b76fe] shadow-[0_12px_24px_rgba(91,118,254,0.10)] transition hover:bg-[#e7ecff] hover:text-[#2a41b6]";
const primaryActionButtonClass =
"border border-[#5b76fe] bg-[#5b76fe] text-white shadow-[0_14px_28px_rgba(91,118,254,0.22)] transition hover:border-[#2a41b6] hover:bg-[#2a41b6]";
"border border-[#5b76fe] bg-[linear-gradient(180deg,#6d87ff_0%,#516bf5_100%)] text-white shadow-[0_16px_30px_rgba(91,118,254,0.22)] transition hover:border-[#2a41b6] hover:bg-[linear-gradient(180deg,#5d78ff_0%,#2a41b6_100%)]";
const compactToolButtonClass =
"h-9 w-9 rounded-[16px] border border-transparent text-[#555a6a] transition hover:border-[#dbe2ff] hover:bg-[#eef2ff] hover:text-[#5b76fe]";
const compactToolButtonActiveClass =
"border-[#dbe2ff] bg-[#eef2ff] text-[#5b76fe] shadow-[0_8px_18px_rgba(91,118,254,0.14)] hover:bg-[#e7ecff] hover:text-[#5b76fe]";
"border-[#dbe2ff] bg-[#eef2ff] text-[#5b76fe] shadow-[0_10px_20px_rgba(91,118,254,0.14)] hover:bg-[#e7ecff] hover:text-[#5b76fe]";
const currentModeLabel =
brushMode === "select"
? "鼠标"
@ -2968,67 +2968,66 @@ export function RealtimeCanvasDialog({
</DialogHeader>
)}
<div className="pointer-events-none absolute top-6 left-6 right-6 z-50 flex flex-wrap items-start gap-3">
<button
className={cn(
"pointer-events-auto flex h-16 items-center gap-2.5 rounded-full px-4 text-foreground transition hover:bg-white",
floatingSurfaceClass
)}
onClick={handleGoHome}
title="返回首页"
type="button"
>
{siteLogo ? (
<img
alt={siteName}
className="h-6 w-6 rounded object-contain"
src={
/^https?:\/\//i.test(siteLogo)
? buildImageProxyUrl(siteLogo)
: siteLogo
}
/>
) : (
<div className="flex h-6 w-6 items-center justify-center rounded bg-[#eef4ff] text-[#1d4ed8]">
<Sparkles className="h-3.5 w-3.5" />
</div>
)}
<span className="font-semibold text-foreground text-sm">
{siteName}
</span>
</button>
<div
className={cn(
"pointer-events-auto inline-flex h-16 w-fit max-w-full items-center gap-1 rounded-full p-1.5",
"pointer-events-auto inline-flex max-w-full items-center gap-2 rounded-[30px] p-2",
floatingSurfaceClass
)}
>
<button
className={cn(
"inline-flex h-full items-center gap-2 rounded-full border px-4 text-sm transition",
workspaceMode === "realtime"
? softActionButtonClass
: "border-transparent bg-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => setWorkspaceMode("realtime")}
className="inline-flex h-12 shrink-0 items-center gap-3 rounded-[22px] border border-white/90 bg-white/80 px-4 text-left text-foreground shadow-[inset_0_1px_0_rgba(255,255,255,0.95)] transition hover:bg-white"
onClick={handleGoHome}
title="返回首页"
type="button"
>
<Paintbrush className="h-4 w-4" />
实时画布模式
</button>
<button
className={cn(
"inline-flex h-full items-center gap-2 rounded-full border px-4 text-sm transition",
workspaceMode === "enhance-pro"
? softActionButtonClass
: "border-transparent bg-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => setWorkspaceMode("enhance-pro")}
type="button"
>
<Wand2 className="h-4 w-4" />
图像增强模式
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-[#eef2ff] shadow-[0_8px_18px_rgba(91,118,254,0.14)]">
{siteLogo ? (
<img
alt={siteName}
className="h-6 w-6 rounded-full object-contain"
src={
/^https?:\/\//i.test(siteLogo)
? buildImageProxyUrl(siteLogo)
: siteLogo
}
/>
) : (
<Sparkles className="h-3.5 w-3.5 text-[#1d4ed8]" />
)}
</span>
<span className="font-semibold text-[14px] tracking-[-0.02em]">
{siteName}
</span>
</button>
<div className="inline-flex h-12 min-w-0 items-center gap-1 rounded-[22px] border border-white/90 bg-white/72 p-1 shadow-[inset_0_1px_0_rgba(255,255,255,0.9)]">
<button
className={cn(
"inline-flex h-full items-center gap-2 rounded-[18px] px-4 text-[14px] font-medium tracking-[-0.02em] transition",
workspaceMode === "realtime"
? softActionButtonClass
: "border border-transparent bg-transparent text-slate-600 hover:bg-white hover:text-foreground"
)}
onClick={() => setWorkspaceMode("realtime")}
type="button"
>
<Paintbrush className="h-4 w-4" />
实时画布模式
</button>
<button
className={cn(
"inline-flex h-full items-center gap-2 rounded-[18px] px-4 text-[14px] font-medium tracking-[-0.02em] transition",
workspaceMode === "enhance-pro"
? softActionButtonClass
: "border border-transparent bg-transparent text-slate-600 hover:bg-white hover:text-foreground"
)}
onClick={() => setWorkspaceMode("enhance-pro")}
type="button"
>
<Wand2 className="h-4 w-4" />
图像增强模式
</button>
</div>
</div>
</div>
{saveProjectDialogOpen ? (